#include "structMesh_testFunctions.hpp"
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>

using Catch::Approx;

TEST_CASE( "True StructEdge: Region of edge indices" ){
	using hms::EdgeRange;

	hms::StructMesh mesh;

	auto test = [](const auto& mesh){
		for (Index i{0}; i < mesh.nEdges(); ++i) {
			EdgeRange r { whichRange(mesh,i).first };
			if (i<12)
				REQUIRE( r == EdgeRange::horizontal );
			else
				REQUIRE( r == EdgeRange::vertical );
		}
	};
	
	SECTION("One ghost layer"){
		test(createStructMesh());
	}
	// SECTION("Two ghost layers"){
	// 	test(createStructMesh());
	// }
}

TEST_CASE( "True StructEdge: vertex indices" ){
		auto test = [](const auto& mesh){
		REQUIRE( (mesh.edge( 0).vertices() == Array2i{  0,  1 }).all() );
		REQUIRE( (mesh.edge( 1).vertices() == Array2i{  1,  2 }).all() );
		REQUIRE( (mesh.edge( 2).vertices() == Array2i{  2,  3 }).all() );
		REQUIRE( (mesh.edge( 3).vertices() == Array2i{  4,  5 }).all() );
		REQUIRE( (mesh.edge( 4).vertices() == Array2i{  5,  6 }).all() );
		REQUIRE( (mesh.edge( 5).vertices() == Array2i{  6,  7 }).all() );
		REQUIRE( (mesh.edge( 6).vertices() == Array2i{  8,  9 }).all() );
		REQUIRE( (mesh.edge( 7).vertices() == Array2i{  9, 10 }).all() );
		REQUIRE( (mesh.edge( 8).vertices() == Array2i{ 10, 11 }).all() );
		REQUIRE( (mesh.edge( 9).vertices() == Array2i{ 12, 13 }).all() );
		REQUIRE( (mesh.edge(10).vertices() == Array2i{ 13, 14 }).all() );
		REQUIRE( (mesh.edge(11).vertices() == Array2i{ 14, 15 }).all() );
		REQUIRE( (mesh.edge(12).vertices() == Array2i{  0,  4 }).all() );
		REQUIRE( (mesh.edge(13).vertices() == Array2i{  1,  5 }).all() );
		REQUIRE( (mesh.edge(14).vertices() == Array2i{  2,  6 }).all() );
		REQUIRE( (mesh.edge(15).vertices() == Array2i{  3,  7 }).all() );
		REQUIRE( (mesh.edge(16).vertices() == Array2i{  4,  8 }).all() );
		REQUIRE( (mesh.edge(17).vertices() == Array2i{  5,  9 }).all() );
		REQUIRE( (mesh.edge(18).vertices() == Array2i{  6, 10 }).all() );
		REQUIRE( (mesh.edge(19).vertices() == Array2i{  7, 11 }).all() );
		REQUIRE( (mesh.edge(20).vertices() == Array2i{  8, 12 }).all() );
		REQUIRE( (mesh.edge(21).vertices() == Array2i{  9, 13 }).all() );
		REQUIRE( (mesh.edge(22).vertices() == Array2i{ 10, 14 }).all() );
		REQUIRE( (mesh.edge(23).vertices() == Array2i{ 11, 15 }).all() );
	};

	SECTION("One ghost layer"){
		test(createStructMesh());
	}
}

TEST_CASE( "True StructEdge: lengths" ){
	auto test = [](const auto& mesh){
		REQUIRE( mesh.edge( 0).length() == Approx( std::sqrt( 29) / 5 )  );
		REQUIRE( mesh.edge( 1).length() == Approx( std::sqrt( 17) / 5 )  );
		REQUIRE( mesh.edge( 2).length() == Approx( std::sqrt(101) / 10)  );
		REQUIRE( mesh.edge( 3).length() == Approx( std::sqrt( 41) / 5 )  );

		REQUIRE( mesh.edge( 4).length() == Approx( 1.4 ));
		REQUIRE( mesh.edge( 5).length() == Approx( std::sqrt(  5) / 5 )  );
		REQUIRE( mesh.edge( 6).length() == Approx( std::sqrt( 29) / 5 )  );
		REQUIRE( mesh.edge( 7).length() == Approx( std::sqrt( 10) / 5 )  );
	
		REQUIRE( mesh.edge( 8).length() == Approx( 1.4 ));
		REQUIRE( mesh.edge( 9).length() == Approx(   1 ));
		REQUIRE( mesh.edge(10).length() == Approx( 1.2 ));
		REQUIRE( mesh.edge(11).length() == Approx(   1 ));

		REQUIRE( mesh.edge(12).length() == Approx( 0.8 ));
		REQUIRE( mesh.edge(13).length() == Approx( 1.2 ));
		REQUIRE( mesh.edge(14).length() == Approx( std::sqrt( 58) / 5 )  );
		REQUIRE( mesh.edge(15).length() == Approx( std::sqrt(  5) / 2 )  );

		REQUIRE( mesh.edge(16).length() == Approx( std::sqrt(  2) ));
		REQUIRE( mesh.edge(17).length() == Approx( std::sqrt(  2) / 5 )  );
		REQUIRE( mesh.edge(18).length() == Approx( std::sqrt( 13) / 5 )  );
		REQUIRE( mesh.edge(19).length() == Approx(   1 ));

		REQUIRE( mesh.edge(20).length() == Approx( std::sqrt( 10) / 5 ));
		REQUIRE( mesh.edge(21).length() == Approx( std::sqrt( 26) / 5 )  );
		REQUIRE( mesh.edge(22).length() == Approx( std::sqrt( 20) / 5 )  );
		REQUIRE( mesh.edge(23).length() == Approx( 0.8 ));
	};

	SECTION("One ghost layer"){
		test(createStructMesh());
	}
}


TEST_CASE("True StructEdge: normals"){

	auto unitnorm =[](double a, double b) -> Vector2s{
		Vector2s vec{a, b};
		return vec.normalized();
	};

	auto test = [&](const auto& mesh){
		REQUIRE( hms::approxEqual( mesh.edge( 0).normal(), unitnorm( -0.4,   1 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge( 1).normal(), unitnorm(  0.2, 0.8 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge( 2).normal(), unitnorm(  0.1,   1 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge( 3).normal(), unitnorm( -0.8,   1 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge( 4).normal(), unitnorm(    0, 1.4 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge( 5).normal(), unitnorm(  0.4, 0.2 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge( 6).normal(), unitnorm(  0.4, 1.0 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge( 7).normal(), unitnorm( -0.2, 0.6 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge( 8).normal(), unitnorm(    0, 1.4 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge( 9).normal(), unitnorm(    0,   1 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge(10).normal(), unitnorm(    0,   1 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge(11).normal(), unitnorm(    0,   1 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge(12).normal(), unitnorm(  0.8,   0 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge(13).normal(), unitnorm(  1.2,   0 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge(14).normal(), unitnorm(  1.4,-0.6 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge(15).normal(), unitnorm(  1.1, 0.2 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge(16).normal(), unitnorm(  1.4,-0.2 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge(17).normal(), unitnorm(  0.2,-0.2 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge(18).normal(), unitnorm(  0.4, 0.6 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge(19).normal(), unitnorm(  0.8,-0.6 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge(20).normal(), unitnorm(  0.6, 0.2 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge(21).normal(), unitnorm(    1, 0.2 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge(22).normal(), unitnorm(  0.8,-0.4 ) ) );
		REQUIRE( hms::approxEqual( mesh.edge(23).normal(), unitnorm(  0.8,   0 ) ) );
	};

	SECTION("One ghost layer"){
		test(createStructMesh());
	}
}

TEST_CASE( "True StructEdge: adjacent cells" ){

	hms::StructMesh mesh;

	auto testOneLayer = [](const auto& mesh){
		REQUIRE( (mesh.edge( 0).cells() == Array2i{  1,  6}).all() );
		REQUIRE( (mesh.edge( 1).cells() == Array2i{  2,  7}).all() );
		REQUIRE( (mesh.edge( 2).cells() == Array2i{  3,  8}).all() );
		REQUIRE( (mesh.edge( 3).cells() == Array2i{  6, 11}).all() );
		REQUIRE( (mesh.edge( 4).cells() == Array2i{  7, 12}).all() );
		REQUIRE( (mesh.edge( 5).cells() == Array2i{  8, 13}).all() );
		REQUIRE( (mesh.edge( 6).cells() == Array2i{ 11, 16}).all() );
		REQUIRE( (mesh.edge( 7).cells() == Array2i{ 12, 17}).all() );
		REQUIRE( (mesh.edge( 8).cells() == Array2i{ 13, 18}).all() );
		REQUIRE( (mesh.edge( 9).cells() == Array2i{ 16, 21}).all() );
		REQUIRE( (mesh.edge(10).cells() == Array2i{ 17, 22}).all() );
		REQUIRE( (mesh.edge(11).cells() == Array2i{ 18, 23}).all() );

		REQUIRE( (mesh.edge(12).cells() == Array2i{  5,  6}).all() );
		REQUIRE( (mesh.edge(13).cells() == Array2i{  6,  7}).all() );
		REQUIRE( (mesh.edge(14).cells() == Array2i{  7,  8}).all() );
		REQUIRE( (mesh.edge(15).cells() == Array2i{  8,  9}).all() );
		REQUIRE( (mesh.edge(16).cells() == Array2i{ 10, 11}).all() );
		REQUIRE( (mesh.edge(17).cells() == Array2i{ 11, 12}).all() );
		REQUIRE( (mesh.edge(18).cells() == Array2i{ 12, 13}).all() );
		REQUIRE( (mesh.edge(19).cells() == Array2i{ 13, 14}).all() );
		REQUIRE( (mesh.edge(20).cells() == Array2i{ 15, 16}).all() );
		REQUIRE( (mesh.edge(21).cells() == Array2i{ 16, 17}).all() );
		REQUIRE( (mesh.edge(22).cells() == Array2i{ 17, 18}).all() );
		REQUIRE( (mesh.edge(23).cells() == Array2i{ 18, 19}).all() );
	};

	auto testTwoLayers = [](const auto& mesh){
		SECTION("First order neighbours"){
			REQUIRE( (mesh.edge( 0).cells(1) == Array2i{ 9,16}).all() );
			REQUIRE( (mesh.edge( 1).cells(1) == Array2i{10,17}).all() );
			REQUIRE( (mesh.edge( 2).cells(1) == Array2i{11,18}).all() );
			REQUIRE( (mesh.edge( 3).cells(1) == Array2i{16,23}).all() );
			REQUIRE( (mesh.edge( 4).cells(1) == Array2i{17,24}).all() );
			REQUIRE( (mesh.edge( 5).cells(1) == Array2i{18,25}).all() );
			REQUIRE( (mesh.edge( 6).cells(1) == Array2i{23,30}).all() );
			REQUIRE( (mesh.edge( 7).cells(1) == Array2i{24,31}).all() ); 
			REQUIRE( (mesh.edge( 8).cells(1) == Array2i{25,32}).all() );
			REQUIRE( (mesh.edge( 9).cells(1) == Array2i{30,37}).all() );
			REQUIRE( (mesh.edge(10).cells(1) == Array2i{31,38}).all() );
			REQUIRE( (mesh.edge(11).cells(1) == Array2i{32,39}).all() );

			REQUIRE( (mesh.edge(12).cells(1) == Array2i{15,16}).all() );
			REQUIRE( (mesh.edge(13).cells(1) == Array2i{16,17}).all() );
			REQUIRE( (mesh.edge(14).cells(1) == Array2i{17,18}).all() );
			REQUIRE( (mesh.edge(15).cells(1) == Array2i{18,19}).all() );
			REQUIRE( (mesh.edge(16).cells(1) == Array2i{22,23}).all() );
			REQUIRE( (mesh.edge(17).cells(1) == Array2i{23,24}).all() );
			REQUIRE( (mesh.edge(18).cells(1) == Array2i{24,25}).all() );
			REQUIRE( (mesh.edge(19).cells(1) == Array2i{25,26}).all() );
			REQUIRE( (mesh.edge(20).cells(1) == Array2i{29,30}).all() );
			REQUIRE( (mesh.edge(21).cells(1) == Array2i{30,31}).all() );
			REQUIRE( (mesh.edge(22).cells(1) == Array2i{31,32}).all() );
			REQUIRE( (mesh.edge(23).cells(1) == Array2i{32,33}).all() );
		}
		SECTION("Second order neighbours"){
			REQUIRE( (mesh.edge( 0).cells(2) == Array2i{ 2,23}).all() );
			REQUIRE( (mesh.edge( 1).cells(2) == Array2i{ 3,24}).all() );
			REQUIRE( (mesh.edge( 2).cells(2) == Array2i{ 4,25}).all() );
			REQUIRE( (mesh.edge( 3).cells(2) == Array2i{ 9,30}).all() );
			REQUIRE( (mesh.edge( 4).cells(2) == Array2i{10,31}).all() );
			REQUIRE( (mesh.edge( 5).cells(2) == Array2i{11,32}).all() );
			REQUIRE( (mesh.edge( 6).cells(2) == Array2i{16,37}).all() );
			REQUIRE( (mesh.edge( 7).cells(2) == Array2i{17,38}).all() );
			REQUIRE( (mesh.edge( 8).cells(2) == Array2i{18,39}).all() );
			REQUIRE( (mesh.edge( 9).cells(2) == Array2i{23,44}).all() );
			REQUIRE( (mesh.edge(10).cells(2) == Array2i{24,45}).all() );
			REQUIRE( (mesh.edge(11).cells(2) == Array2i{25,46}).all() );

			REQUIRE( (mesh.edge(12).cells(2) == Array2i{14,17}).all() );
			REQUIRE( (mesh.edge(13).cells(2) == Array2i{15,18}).all() );
			REQUIRE( (mesh.edge(14).cells(2) == Array2i{16,19}).all() );
			REQUIRE( (mesh.edge(15).cells(2) == Array2i{17,20}).all() );
			REQUIRE( (mesh.edge(16).cells(2) == Array2i{21,24}).all() );
			REQUIRE( (mesh.edge(17).cells(2) == Array2i{22,25}).all() );
			REQUIRE( (mesh.edge(18).cells(2) == Array2i{23,26}).all() );
			REQUIRE( (mesh.edge(19).cells(2) == Array2i{24,27}).all() );
			REQUIRE( (mesh.edge(20).cells(2) == Array2i{28,31}).all() );
			REQUIRE( (mesh.edge(21).cells(2) == Array2i{29,32}).all() );
			REQUIRE( (mesh.edge(22).cells(2) == Array2i{30,33}).all() );
			REQUIRE( (mesh.edge(23).cells(2) == Array2i{31,34}).all() );
		}	
	};

	SECTION("One ghost layer"){
		testOneLayer(createStructMesh());
	}
}

TEST_CASE( "True StructEgde: midpoints" ){

	auto test = [](const auto& mesh){
		REQUIRE( hms::approxEqual(mesh.edge( 0).midpoint(), Vector2s{ 0.5, 0.2  }) );
		REQUIRE( hms::approxEqual(mesh.edge( 1).midpoint(), Vector2s{ 1.4, 0.3  }) );
		REQUIRE( hms::approxEqual(mesh.edge( 2).midpoint(), Vector2s{ 2.3, 0.15 }) );
		REQUIRE( hms::approxEqual(mesh.edge( 3).midpoint(), Vector2s{ 0.5, 1.2  }) );
		REQUIRE( hms::approxEqual(mesh.edge( 4).midpoint(), Vector2s{ 1.7, 1.6  }) );
		REQUIRE( hms::approxEqual(mesh.edge( 5).midpoint(), Vector2s{ 2.5, 1.4  }) );
		REQUIRE( hms::approxEqual(mesh.edge( 6).midpoint(), Vector2s{ 0.7, 2    }) );
		REQUIRE( hms::approxEqual(mesh.edge( 7).midpoint(), Vector2s{ 1.5, 1.9  }) );
		REQUIRE( hms::approxEqual(mesh.edge( 8).midpoint(), Vector2s{ 2.5, 2    }) );
		REQUIRE( hms::approxEqual(mesh.edge( 9).midpoint(), Vector2s{ 0.5, 2.8  }) );
		REQUIRE( hms::approxEqual(mesh.edge(10).midpoint(), Vector2s{ 1.6, 2.8  }) );
		REQUIRE( hms::approxEqual(mesh.edge(11).midpoint(), Vector2s{ 2.7, 2.8  }) );
		REQUIRE( hms::approxEqual(mesh.edge(12).midpoint(), Vector2s{ 0  , 0.4  }) );
		REQUIRE( hms::approxEqual(mesh.edge(13).midpoint(), Vector2s{ 1  , 1    }) );
		REQUIRE( hms::approxEqual(mesh.edge(14).midpoint(), Vector2s{ 2.1, 0.9  }) );
		REQUIRE( hms::approxEqual(mesh.edge(15).midpoint(), Vector2s{ 2.7, 0.65 }) );
		REQUIRE( hms::approxEqual(mesh.edge(16).midpoint(), Vector2s{ 0.1, 1.5  }) );
		REQUIRE( hms::approxEqual(mesh.edge(17).midpoint(), Vector2s{ 1.1, 1.7  }) );
		REQUIRE( hms::approxEqual(mesh.edge(18).midpoint(), Vector2s{ 2.1, 1.8  }) );
		REQUIRE( hms::approxEqual(mesh.edge(19).midpoint(), Vector2s{ 2.9, 1.6  }) );
		REQUIRE( hms::approxEqual(mesh.edge(20).midpoint(), Vector2s{ 0.1, 2.5  }) );
		REQUIRE( hms::approxEqual(mesh.edge(21).midpoint(), Vector2s{ 1.1, 2.3  }) );
		REQUIRE( hms::approxEqual(mesh.edge(22).midpoint(), Vector2s{ 2  , 2.4  }) );
		REQUIRE( hms::approxEqual(mesh.edge(23).midpoint(), Vector2s{ 3.2, 2.4  }) );
	};

	SECTION("One ghost layer"){
		test(createStructMesh());
	}
}
